#include "Engine.h"
#include "Keyboard.h"
#include "Mouse.h"
#include "OpenGL.h"
#include "Window.h"
#include "Application.h"

Engine::Engine()
{	
}

Engine::~Engine()
{	
	Destroy();
}

bool Engine::Init(int screenWidth, int screenHeight)
{
	Keyboard::Reset();			
	Mouse::Reset();
	Box::SetWidth(0.4f);
	DynamicSphere::SetRadius(0.05f);

	if(!Map::Load(L"Map.ini", &map))
		return false;	
	scene.Init(&map);
	OpenGL::Init(screenWidth, screenHeight, map, &spheres);
	
	cameraSphere.SetPosition(OpenGL::GetCamera().Position());
	cameraSphere.SetVelocity(Vector3(0, 0, 0));
	scene.SetCameraSphere(&cameraSphere);
	
	ai.Init(&scene, &map, scene.Border());	

	Boid *b;
	for(int i = 0; i < 200; i++)
	{
		b = new Boid();	
		b->SetPosition(Vector3(	map.Columns() * (float)rand() / (float)RAND_MAX,
								map.GetHighest() * (float)rand() / (float)RAND_MAX,
								map.Rows() * (float)rand() / (float)RAND_MAX));
		b->SetVelocity(Vector3(	-1.0f + 2.0f * (float)rand() / (float)RAND_MAX,
								-1.0f + 2.0f * (float)rand() / (float)RAND_MAX,
								-1.0f + 2.0f * (float)rand() / (float)RAND_MAX));
		b->SetType(Boid::BoidType::Yellow);
		b->SetEnergy(0.0f);

		spheres.push_back(b);
		scene.AddSphere((DynamicSphere*)b);
		ai.AddBoid(b);		
	}			
	return true;
}

void Engine::Maintain(void)
{
	static unsigned int lastTime = GetTickCount();
	static unsigned int lastFpsTime = GetTickCount();
	static int fpsCount = 0;
	unsigned int currentTime;
	float frameTime;
	currentTime = GetTickCount();
	frameTime = (currentTime - lastTime) * 0.001f;
	lastTime = currentTime;
	if(frameTime > 0.1f)
		frameTime = 0.1f;
	static wchar_t buf[20];
	if(currentTime - lastFpsTime > 3000)
	{
		float fps = 1000.0f * (float)fpsCount / (float)(currentTime - lastFpsTime);
		fpsCount = 0;		
		swprintf(buf, 100, L"AI %3.1f fps", fps);
		SetWindowText(Window::MainWindow().Wnd(), buf);
		lastFpsTime = currentTime;
	}
	fpsCount++;

	//Keyboard
	static bool wasLeftPressed = false;
	if((Mouse::Left()) && (!wasLeftPressed))
	{
		wasLeftPressed = true;
		Boid *boid = new Boid();
		boid->SetPosition(OpenGL::GetCamera().Position() + 0.5f * OpenGL::GetCamera().GetLookingDirection());
		boid->SetVelocity(5.0f * OpenGL::GetCamera().GetLookingDirection());
		boid->SetType(Boid::BoidType::Red);
		boid->SetEnergy(Boid::RedStartEnergy());
		spheres.push_back(boid);
		scene.AddSphere(boid);
		ai.AddBoid(boid);
	}
	if(!Mouse::Left())
		wasLeftPressed = false;

	static bool wasRightPressed = false;
	if((Mouse::Right()) && (!wasRightPressed))
	{
		wasRightPressed = true;
		Boid *boid = new Boid();
		boid->SetPosition(OpenGL::GetCamera().Position() + 0.5f * OpenGL::GetCamera().GetLookingDirection());
		boid->SetVelocity(5.0f * OpenGL::GetCamera().GetLookingDirection());
		boid->SetType(Boid::BoidType::Yellow);
		spheres.push_back(boid);
		scene.AddSphere(boid);
		ai.AddBoid(boid);
	}
	if(!Mouse::Right())
		wasRightPressed = false;

	if (Keyboard::NewKeyPressed('R'))
	{
		Vector3 positionDiff = 0.25f * OpenGL::GetCamera().GetLookingDirection();
		Vector3 newBoidPosition = OpenGL::GetCamera().Position();
		for (int i=0; i < 50; i++)
		{
			Boid *boid = new Boid();
			boid->SetPosition(newBoidPosition);
			boid->SetVelocity(5.0f * OpenGL::GetCamera().GetLookingDirection());
			boid->SetType(Boid::BoidType::Red);
			boid->SetEnergy(Boid::RedStartEnergy());
			spheres.push_back(boid);
			scene.AddSphere(boid);
			ai.AddBoid(boid);
			newBoidPosition += positionDiff;
		}
	}

	if (Keyboard::NewKeyPressed('F'))
	{
		Vector3 newBoidPosition = OpenGL::GetCamera().Position() + 0.50f * OpenGL::GetCamera().GetLookingDirection();;
		for (int i=0; i < 50; i++)
		{
			Boid *boid = new Boid();
			boid->SetPosition(newBoidPosition + Vector3((rand()%10)/10.0f, (rand()%10)/10.0f, (rand()%10)/10.0f));
			boid->SetVelocity(15.0f * OpenGL::GetCamera().GetLookingDirection());
			boid->SetType(Boid::BoidType::Yellow);
			spheres.push_back(boid);
			scene.AddSphere(boid);
			ai.AddBoid(boid);
		}
	}

	//Update keyboard state
	Keyboard::Update();

	//Render scene
	OpenGL::UpdateCamera(frameTime);
	cameraSphere.SetPosition(OpenGL::GetCamera().Position());
	scene.Simulate(frameTime);
	OpenGL::GetCamera().SetPosition(cameraSphere.Position());
	ai.Maintain(&scene, frameTime);

	//Remove eaten boids	
	list<DynamicSphere*>* eatenBoids = ai.EatenBoids();
	list<DynamicSphere*>::iterator eaten;
	for(eaten = eatenBoids->begin(); eaten != eatenBoids->end(); ++eaten)
	{
		scene.RemoveSphere(*eaten);
		ai.RemoveBoid((Boid*)(*eaten));
		spheres.remove((Boid*)*eaten);
		delete *eaten;
	}
	eatenBoids->clear();

	OpenGL::Render();			
	SwapBuffers(Window::MainWindow().Dc());

	if(Keyboard::KeyPressed(VK_ESCAPE))
	{
		Application::SetWorking(false);
		return;
	}

#if _DEBUG
	PrintInfo();
#endif
}

void Engine::Destroy(void)
{
	OpenGL::Destroy();
	ai.Destroy();
	list<Boid*>::iterator boid;
	for(boid = spheres.begin(); boid != spheres.end(); ++boid)
	{
		delete (*boid);
		(*boid) = NULL;
	}
	spheres.clear();		
}

void Engine::PrintInfo(void)
{
	static int engineLastSphereCount = 0;
	if (spheres.size() != engineLastSphereCount)
	{
		engineLastSphereCount = (int)spheres.size();
		printf("Engine spheres' count changed: %d\r\n", spheres.size());
	}
	static int physicsLastSphereCount = 0;
	if (scene.GetSpheresCount() != physicsLastSphereCount)
	{
		physicsLastSphereCount = scene.GetSpheresCount();
		printf("Physics spheres' count changed: %d\r\n", scene.GetSpheresCount());
	}
	static int physicsLastSphereMapCount = 0;
	if (scene.GetSphereMapCount() != physicsLastSphereMapCount)
	{
		physicsLastSphereMapCount = scene.GetSphereMapCount();
		printf("Physics SphereMap's count changed: %d\r\n", scene.GetSphereMapCount());
	}
}
